#pragma once

#include <unordered_map>
#include <vector>
#include <SFML/Graphics.hpp>
#include <algorithm>
#include "vec2.cpp"

#define PI (3.14159265358979)

int free_room_id = 0;

class Room {
    public:
    
    int connected_rooms[4];
    int backwards_doors[4];
    std::unordered_map<int, Room> * rooms;
    int id;
    sf::Color color;
    sf::RenderTexture texture;
    std::vector<sf::Shape*> objects;
    
    void add_object(sf::Shape* object) { // Add only long-living pointers and delete those manually!!!
        if(std::find(objects.begin(), objects.end(), object) == objects.end())
            objects.push_back(object);
    }

    void remove_object(sf::Shape* object) {
        objects.erase(std::remove(objects.begin(), objects.end(), object), objects.end());
    }

    Room() {
        id = -1;
        connected_rooms[0] = -1; connected_rooms[1] = -1; connected_rooms[2] = -1; connected_rooms[3] = -1;
        backwards_doors[0] = -1; backwards_doors[1] = -1; backwards_doors[2] = -1; backwards_doors[3] = -1;
    }

    static Room & new_room(std::unordered_map<int, Room> * rooms, sf::Color color, int room_size) {
        int id = free_room_id++;
        rooms->try_emplace(id);
        Room & room = rooms->find(id)->second;
        room.id = id;
        room.color = color;
        room.rooms = rooms;
        room.texture.create(room_size, room_size);
        return room;
    }

    void clear() {
        texture.clear(color);
    }

    void apply_objects() {
        for(sf::Shape * object : objects) {
            sf::Vector2f object_position = object->getPosition();
            float object_rotation = object->getRotation();
            texture.draw(*object);
            sf::FloatRect neighbours[] = {
                {0, (float)texture.getSize().y, (float)texture.getSize().x, (float)texture.getSize().y},
                {(float)texture.getSize().x, 0, (float)texture.getSize().x, (float)texture.getSize().y},
                {0, -(float)texture.getSize().y, (float)texture.getSize().x, (float)texture.getSize().y},
                {-(float)texture.getSize().x, 0, (float)texture.getSize().x, (float)texture.getSize().y},
            };
            for(int i = 0; i < 4; i ++) {
                if(neighbours[i].intersects(object->getGlobalBounds()) && connected_rooms[i] >= 0) {
                    vec2 pos = inv_y((vec2)object_position / texture.getSize().x);
                    sf::Vector2f new_position = (inv_y(transform_to_room(pos, i)) * texture.getSize().x).sf();
                    object->setPosition(new_position);
                    object->setRotation(object_rotation + ((i + 6 - backwards_doors[i]) % 4) * 90);
                    (*rooms)[connected_rooms[i]].texture.draw(*object);
                    object->setPosition(object_position);
                    object->setRotation(object_rotation);
                }
            }
        }
    }

    int relative_rotatation(int door) {
        return (door + 6 - backwards_doors[door]) % 4;
    }

    vec2 transform_to_room(vec2 p, int door) {
        p -= rotate(vec2(0., -1.), door * PI / 2);
        return vec2(.5, .5) + rotate(p - vec2(.5, .5), -relative_rotatation(door) * PI / 2);
    }

    void disconnect(int door) {
        (*rooms)[connected_rooms[door]].connected_rooms[backwards_doors[door]] = -1;
        (*rooms)[connected_rooms[door]].backwards_doors[backwards_doors[door]] = -1;
        connected_rooms[door] = -1;
        backwards_doors[door] = -1;
    }

    Room(const Room &) = delete;
    void operator=(const Room &x) = delete;
};

// Room* null_room = new Room();

void connect(Room & room1, int door1to2, Room & room2, int door2to1) {
    if(room1.connected_rooms[door1to2] >= 0)
        room1.disconnect(door1to2);
    if(room2.connected_rooms[door2to1] >= 0)
        room2.disconnect(door2to1);
    room1.connected_rooms[door1to2] = room2.id;
    room1.backwards_doors[door1to2] = door2to1;
    room2.connected_rooms[door2to1] = room1.id;
    room2.backwards_doors[door2to1] = door1to2;
}
